# -*- coding: utf-8-sig -*-
from xml.dom.minidom import parse, parseString, Document
from django.utils.encoding import smart_str

class XMLDeporte():
    '''Describe al tag de Deporte en un XML

    Parámetros:
    nombre -- cadena de longitud 30 que representa el nombre del deporte
    datos_requeridos -- lista de cadenas con los datos requeridos para federarse a un deporte
    disciplinas -- lista de disciplinas que contiene un deporte
    '''
    
    def __init__(self,nombre='', datos_requeridos=[],pk=0, id=''):
        self.nombre = nombre
        self.disciplinas = []
        self.datos_requeridos = datos_requeridos
        
        #para generar los identificadores de los componentes
        self.checked = 'no'
        self.contiene_resultados = False
        self.pk = pk
        self.id = id
    
    def setId(self,pk=0):
        self.pk = pk
        self.id = 'D-'+str(pk)
        dep = self.id
        for disciplina in self.disciplinas:
            disc = dep+'-'+str(self.disciplinas.index(disciplina))
            disciplina.pk = self.disciplinas.index(disciplina)
            disciplina.id = disc

            for categoria in disciplina.categorias:
                cat = disc+'-'+str(disciplina.categorias.index(categoria))
                categoria.id = cat
                categoria.pk = disciplina.categorias.index(categoria)
    
    
    def setNombreDom(self,deporte_dom):
        '''
        Asigna el nombre del deporte.
        Retorna None en situación de error.
        
        Parámetros:
        deporte_dom -- contiene un objeto de minidom para obtener el valor del nombre
        '''
        try:
            self.nombre = deporte_dom.attributes['nombre'].value
        except:
            return None
        try:
            self.pk = int(deporte_dom.attributes['pk'].value)
        except:
            return None
        try:
            self.id = deporte_dom.attributes['id'].value
        except:
            return None
    
    def setDatos_requeridosDom(self,deporte_dom):
        '''
        Asigna la lista de datos requeridos que posee el deporte.
        Retorna None en situación de error.
        
        Parámetros:
        deporte_dom -- contiene un objeto de minidom para obtener el valor del nombre
        '''
        try:
            datos_dom = deporte_dom.getElementsByTagName('datos_requeridos')[0].getElementsByTagName('dato')
        except:
            return None
        self.datos_requeridos = []
        for dato in datos_dom:
            dato_str = dato.childNodes[0].data
            self.datos_requeridos.append(dato_str)
    
    def setDisciplinasDom(self, deporte_dom):
        '''
        Asigna la lista de disciplinas de los deportes con objetos XMLDisciplina.
        Retorna None en situación de error.
        
        Parámetros:
        deporte_dom -- contiene un objeto de minidom para obtener el valor las disciplinas
        '''
        try:
            disciplinas = deporte_dom.getElementsByTagName('disciplinas')[0].getElementsByTagName('disciplina')
        except:
            return None
        for disciplina in disciplinas:
            xml_disciplina = XMLDisciplina(deporte=self)
            #cambiar el nombre de la disciplina
            xml_disciplina.setNombreDom(disciplina)
            #agregar a la lista de categorías de la disciplina
            xml_disciplina.setCategoriasDom(disciplina)
            
            #agregar a la lista de disciplinas
            self.disciplinas.append(xml_disciplina)
    
    def toxml(self):
        '''
        Retorna una cadena con el xml que representa a la estructura.
        Retorna None en situación de error.
        
        '''
        
        #crea un documento de minidom
        doc = Document()
        #crear el tag <deportes>
        #deportes = doc.createElement("deportes")
        #doc.appendChild(deportes)
        
        #crear el tag <deporte nombre = "nombre">
        deporte = doc.createElement("deporte")
        deporte.setAttribute("nombre",self.nombre)
        deporte.setAttribute("pk",str(self.pk))
        deporte.setAttribute("id",self.id)
        doc.appendChild(deporte)
        
        #crear el tag <datos_requeridos>
        doc_datos_requeridos = doc.createElement("datos_requeridos")
        deporte.appendChild(doc_datos_requeridos)
        for dato in self.datos_requeridos:
            #crear tag: <dato>dato_txt</dato>
            doc_dato = doc.createElement("dato")
            doc_dato.appendChild(doc.createTextNode(dato))
            doc_datos_requeridos.appendChild(doc_dato)
        
        #crear el tag <disciplinas>
        doc_disciplinas = doc.createElement("disciplinas")
        deporte.appendChild(doc_disciplinas)
        
        for disciplina in self.disciplinas:
            #crear el tag de cada disciplina
            doc_disciplina = doc.createElement("disciplina")
            doc_disciplina.setAttribute("nombre",disciplina.nombre)
            doc_disciplina.setAttribute("pk",str(disciplina.pk))
            doc_disciplina.setAttribute("id",disciplina.id)
            doc_disciplinas.appendChild(doc_disciplina)
            
            #crear el tag de categorías
            doc_categorias = doc.createElement("categorias")
            doc_categorias.setAttribute("sexo",str(disciplina.tiene_sexo))
            doc_categorias.setAttribute("edad",str(disciplina.tiene_edad))
            doc_categorias.setAttribute("peso",str(disciplina.tiene_peso))
            doc_categorias.setAttribute("tipo_resultado",disciplina.tipo_resultado)
            doc_disciplina.appendChild(doc_categorias)
            
            #llenar la categoría para cada disciplina
            for categoria in disciplina.categorias:
                doc_categoria = doc.createElement("categoria")
                doc_categoria.setAttribute("nombre",categoria.nombre)
                doc_categoria.setAttribute("pk",str(categoria.pk))
                doc_categoria.setAttribute("id",categoria.id)
                doc_categorias.appendChild(doc_categoria)
                
                #Si posee sexo
                if disciplina.tiene_sexo:
                    doc_sexo = doc.createElement("sexo")
                    doc_sexo.setAttribute("valor",categoria.sexo)
                    doc_categoria.appendChild(doc_sexo)
                
                #Si posee edad
                if disciplina.tiene_edad:
                    doc_edad = doc.createElement("edad")
                    doc_edad.setAttribute("nombre",categoria.edad.nombre)
                    doc_edad.setAttribute("min",str(categoria.edad.min))
                    doc_edad.setAttribute("max",str(categoria.edad.max))
                    doc_categoria.appendChild(doc_edad)
                
                #Si posee peso
                if disciplina.tiene_peso:
                    doc_peso = doc.createElement("peso")
                    doc_peso.setAttribute("nombre",categoria.peso.nombre)
                    doc_peso.setAttribute("min",str(categoria.peso.min))
                    doc_peso.setAttribute("max",str(categoria.peso.max))
                    doc_peso.setAttribute("dimensional",categoria.peso.dimensional)
                    doc_categoria.appendChild(doc_peso)
                
                #agregar lista de resultados
                for resultado in categoria.resultados:
                    doc_resultado = doc.createElement("resultado")
                    doc_resultado.setAttribute("atleta",str(resultado.atleta))
                    doc_resultado.setAttribute("posicion",resultado.posicion)
                    doc_resultado.setAttribute("valor",resultado.valor)
                    doc_categoria.appendChild(doc_resultado)
        
        #la cadena de retorno incluye desde el tag: <deporte nombre=''> hasta </deporte>
        return doc.toxml('UTF-8').split("?>",2)[1]
    
    
    def __unicode__(self):
        return self.nombre



class XMLDisciplina():
    '''Describe al tag de Disciplina en un XML

    Parámetros:
    nombre -- cadena de longitud 30 que representa el nombre una disciplina
    categorias -- lista de XMLCategorias con las categorías que contiene una disciplina
    tipo_resultado -- cadena que contiene el tipo de resultado almacenado en el valor del tag resultado (tiempo, peso, alcance, etc)
    tiene_sexo -- booleano que dice si agrupar las categorías de la disciplina por sexo
    tiene_edad -- booleano que dice si agrupar las categorías de la disciplina por edad
    tiene_peso -- booleano que dice si agrupar las categorías de la disciplina por peso
    '''
    def __init__(self, nombre='', deporte=XMLDeporte(), pk=0, id=''):
        self.deporte = deporte
        self.nombre = nombre
        self.categorias = []
        self.tipo_resultado = ''
        self.tiene_sexo = True
        self.tiene_edad = True
        self.tiene_peso = True
        
        #para generar los identificadores de los componentes
        self.pk = int(pk)
        if id=='':
            self.id = deporte.id +'-'+ str(pk)
        else:
            self.id = id
        self.checked = 'no'
        self.contiene_resultados = False
    
    def getCategoriaFiltro(self,nombre='',sexo='',edad_nombre='',peso_nombre=''):
        z = []
        for cat in self.categorias:
            bool_nombre = True if nombre=='' else (cat.nombre==nombre)
            if not (bool_nombre):
                continue
            bool_sexo = True if sexo=='' else (cat.sexo==sexo)
            if not (bool_sexo):
                continue
            bool_edad = True if edad_nombre=='' else (cat.edad.nombre==edad)
            if not (bool_edad):
                continue
            bool_peso = True if peso_nombre=='' else (cat.peso.nombre==peso)
            if not (bool_peso):
                continue
            z.append(cat)
        return z
        
    
    
    def setNombreDom(self,disciplina_dom):
        '''
        Asigna el nombre de la disciplina
        Retorna None en situación de error.
        
        Parámetros:
        disciplina_dom -- contiene un objeto de minidom para obtener el valor del nombre
        '''
        try:
            self.nombre = disciplina_dom.attributes['nombre'].value
        except:
            return None
        try:
            self.pk = int(disciplina_dom.attributes['pk'].value)
        except:
            return None
        try:
            self.id = disciplina_dom.attributes['id'].value
        except:
            return None
    
    def setCategoriasDom(self,disciplina_dom):
        '''
        Asigna la lista de categorías de los deportes con objetos XMLCategoria.
        Retorna None en situación de error.
        
        Parámetros:
        disciplina_dom -- contiene un objeto de minidom para obtener el valor las categorías
        '''
        try:
            categorias_dom = disciplina_dom.getElementsByTagName('categorias')[0]
            self.tiene_sexo = ((categorias_dom.attributes['sexo'].value)=='True')
            self.tiene_edad = ((categorias_dom.attributes['edad'].value)=='True')
            self.tiene_peso = ((categorias_dom.attributes['peso'].value)=='True')
            self.tipo_resultado = categorias_dom.attributes['tipo_resultado'].value
            categoria_dom = categorias_dom.getElementsByTagName('categoria')
        except:
            return None
        
        for cat in categoria_dom:
            
            nombre_categoria = cat.attributes['nombre'].value
            try:
                pk = int(cat.attributes['pk'].value)
            except:
                pk = 0
            id = cat.attributes['id'].value
            categoria = XMLCategoria(nombre=nombre_categoria,pk=pk,id=id,disciplina=self)
            
            #si se cuenta el sexo
            if self.tiene_sexo:
                try:
                    cat_sexo = cat.getElementsByTagName('sexo')[0]
                    categoria.sexo = cat_sexo.attributes['valor'].value
                except:
                    self.tiene_sexo = False
            
            #si se cuenta la edad
            if self.tiene_edad:
                try:
                    cat_edad = cat.getElementsByTagName('edad')[0]
                    cat_edad_nombre = cat_edad.attributes['nombre'].value
                    cat_edad_max = cat_edad.attributes['max'].value
                    cat_edad_min = cat_edad.attributes['min'].value
                    categoria.edad = XMLEdad(nombre=cat_edad_nombre,min=cat_edad_min,max=cat_edad_max)
                except:
                    self.tiene_edad = False
                    
            #si se cuenta el peso
            if self.tiene_peso:
                try:
                    cat_peso = cat.getElementsByTagName('peso')[0]
                    cat_peso_nombre = cat_peso.attributes['nombre'].value
                    cat_peso_max = cat_peso.attributes['max'].value
                    cat_peso_min = cat_peso.attributes['min'].value
                    cat_peso_dim = cat_peso.attributes['dimensional'].value
                    categoria.peso = XMLPeso(nombre=cat_peso_nombre,min=cat_peso_min,max=cat_peso_max, dimensional=cat_peso_dim)
                except:
                    self.tiene_peso = False
            
            categoria.set_nombre_completo()
            #Asignar resultados
            categoria.setResultadosDom(cat)
            
            #agregar a la lista de categorias
            self.categorias.append(categoria)
        

    def __unicode__(self):
        return self.nombre

class XMLCategoria:
    '''Describe al tag de Categoría en un XML

    Parámetros:
    nombre -- cadena de longitud 30 que representa el nombre de la categoría
    resultados -- lista de cadenas con las categorías que contiene una disciplina
    '''
    def __init__(self,nombre='',sexo='',edad=None,peso=None,disciplina=XMLDisciplina(),id='', pk=0):
        self.disciplina = disciplina
        self.nombre = nombre
        self.resultados = []
        self.edad = edad
        self.sexo = sexo
        self.peso = peso
        
        #para generar los identificadores de los componentes
        self.pk = pk
        if id=='':
            self.id = disciplina.id +'-'+str(pk)
        else:
            self.id = id
        
        self.checked = 'no'
        self.contiene_resultados = False
        #set_nombre_completo()
    
    def set_nombre_completo(self):
        txt = self.nombre+' '
        if not self.sexo=='':
            txt+= self.sexo+' '
        if not self.edad==None:
            txt+= self.edad.nombre+' ('+str(self.edad.min)+'-'+str(self.edad.max)+u' años) '
        if not self.peso==None:
            txt+= self.peso.nombre+' ('+str(self.peso.min)+'-'+str(self.peso.max)+' '+self.peso.dimensional+') '
        #print txt
        self.nombre_completo = txt
        
        
    def setNombreDom(self,categoria_dom):
        '''
        Asigna el nombre de la categoría.
        Retorna None en situación de error.
        
        Parámetros:
        categoria_xml -- contiene un objeto de minidom para obtener el valor del nombre
        '''
        try:
            self.nombre = categoria_dom.attributes['nombre'].value
        except:
            return None
        try:
            self.pk = int(categoria_dom.attributes['pk'].value)
        except:
            return None
        try:
            self.id = categoria_dom.attributes['id'].value
        except:
            return None
    
    
    def setResultadosDom(self,categoria_dom):
        '''
        Asigna la lista de disciplinas de los deportes con cadenas de resultados.
        Retorna None en situación de error.
        
        Parámetros:
        categoria_dom -- contiene un objeto de minidom para obtener el valor los resultados
        '''
        try:
            resultados_dom = categoria_dom.getElementsByTagName('resultado')
            for result in resultados_dom:
                res = XMLResultado(atleta = result.attributes['atleta'].value ,posicion = result.attributes['posicion'].value, valor = result.attributes['valor'].value)
                self.resultados.append(res)
        except:
            return None
    
    def validar(self, atleta):
        valido = True
        msj = 'El atleta \''+atleta.nombres+'\' no se ha podido inscribir a \''+self.nombre_completo+'\''
        
        if not self.sexo=='':
            if self.sexo.lower()!=atleta.sexo.lower() and self.sexo.lower()!='mixto':
                valido = False
                msj+=', sexo incorrecto'
                
        if not self.edad==None:
            res = self.edad.validar(atleta)
            if not res:
                valido = False
                msj+=u', edad no válida ('+str(atleta.get_edad())+u' años)'
               
        if not self.peso==None:
            res = self.peso.validar(atleta)
            if not res:
                valido = False
                msj+=u', peso no válido ('+str(atleta.peso)+')'
        
        return valido, msj
        
    
    def __unicode__(self):
        return (self.sexo+" "+self.nombre+" "+self.edad+" "+self.peso)





class XMLEdad:
    '''Describe al tag de Edad en un XML dentro de una categoría. Representa un rango de edad. Ejemplo: Infantil A, 10-12 años

    Parámetros:
    nombre -- cadena de longitud 30 que representa el nombre de la edad. Ejemplo: Infantil A, Juvenil
    max -- número que representa la edad máxima que permitida para este rango de edad. (inclusive)
    min -- número que representa la edad mínima que permitida para este rango de edad. (inclusive)
    '''
    def __init__(self,nombre='',max=200,min=0):
        self.nombre = nombre
        self.max = int(max)
        self.min = int(min)
    
    def validar(self, atleta):
        edad_atleta = atleta.get_edad()
        #print 'validar: edad atleta:',edad_atleta
        #print 'validar: types min,max,edad:',type(self.min),type(self.max),type(edad_atleta)
        #print 'validar: min, max:',self.min,self.max
        #print 'validar: edad_atleta<=self.max:',edad_atleta<=self.max
        #print 'validar: edad_atleta>=self.min:',(self.min - edad_atleta)<=0
        
        if edad_atleta<=self.max and self.min<=edad_atleta:
            return True
        return False
    
    def __unicode__(self):
        return self.nombre+","+str(self.min)+"-"+str(self.max)+u" años"



class XMLPeso:
    '''Describe al tag de Peso en un XML dentro de una categoría. Representa un rango de peso. Ejemplo: Infantil A, 10-12 años

    Parámetros:
    nombre -- cadena de longitud 30 que representa el nombre del peso. Ejemplo: Infantil A, Juvenil
    max -- número que representa el peso máximo que permitida para este rango de peso. (inclusive)
    min -- número que representa el peso mínimo que permitida para este rango de peso. (inclusive)
    '''
    def __init__(self,nombre='',max=800,min=0,dimensional=''):
        self.nombre = nombre
        self.max = int(max)
        self.min = int(min)
        self.dimensional = dimensional
    
    def validar(self, atleta):
        peso_atleta = atleta.peso
        if peso_atleta<=self.max and peso_atleta>=self.min:
            return True
        return False
    
    def __unicode__(self):
        return self.nombre


class XMLResultado:
    '''Describe al tag de Resultado en un XML dentro de una categoría.

    Parámetros:
    posicion -- número que contiene la posición obtenida en una categoría
    valor -- cadena que representa el resultado (puntos, tiempo, distancia, etc) dependiendo de lo que se mida en la categoría
    min -- número que representa la edad mínima que permitida para este rango de edad. (inclusive)
    '''
    def __init__(self,atleta='',posicion='',valor=''):
        self.atleta = int(atleta)
        self.posicion = posicion
        self.valor = valor
    
    def __unicode__(self):
        return 'Puesto: '+str(self.posicion)+', resultado: '+self.valor+''


def set_checked(lista_1=[], lista_2=[]):
    #lista_2 = todos
    #lista_1 = parcial
    for dep_2 in lista_2:
        for dep_1 in lista_1:
            if dep_2.id == dep_1.id:
                
                for dis_2 in dep_2.disciplinas:
                    for dis_1 in dep_1.disciplinas:
                        if dis_1.id == dis_2.id:
                            
                            for cat_2 in dis_2.categorias:
                                for cat_1 in dis_1.categorias:
                                    if cat_1.id==cat_2.id:
                                        cat_1.checked='yes'
                            dis_1.checked='yes'
                        
                dep_1.checked='yes'
    
    return lista_1


def validar_atleta(atleta, lista_deportes=[]):
    errores = []
    
    for deporte in lista_deportes:
        for disciplina in deporte.disciplinas:
            for categoria in disciplina.categorias:
                valido,msj = categoria.validar(atleta)
                if not valido:
                    errores.append(msj)
    
    return (len(errores)==0), errores

def request_xml(request, lista_deportes=[], get_lista = False):
    nueva_lista = []
    for deporte in lista_deportes:
        
        if deporte.id in request.POST:
            #crear el deporte con todos los atributos, menos las disciplinas
            n_deporte = XMLDeporte(nombre=deporte.nombre, pk=deporte.pk, id=deporte.id)
            for disciplina in deporte.disciplinas:
                if disciplina.id in request.POST:
                    #crear la disciplina con todos los atributos, menos las categorias
                    n_disciplina = XMLDisciplina(nombre=disciplina.nombre,deporte=n_deporte, id=disciplina.id, pk=disciplina.pk)
                    n_disciplina.tipo_resultado = disciplina.tipo_resultado
                    n_disciplina.tiene_edad = disciplina.tiene_edad
                    n_disciplina.tiene_peso = disciplina.tiene_peso
                    n_disciplina.tiene_sexo = disciplina.tiene_sexo
                    
                    for categoria in disciplina.categorias:
                        if categoria.id in request.POST:
                            #crear la disciplina con todos los atributos, menos las categorias
                            n_categoria = XMLCategoria(nombre=categoria.nombre,disciplina=n_disciplina, id=categoria.id, pk=categoria.pk)
                            n_categoria.edad = categoria.edad
                            n_categoria.sexo = categoria.sexo
                            n_categoria.peso = categoria.peso
                            n_categoria.set_nombre_completo()
                            n_disciplina.categorias.append(n_categoria)
                    n_deporte.disciplinas.append(n_disciplina)        
            nueva_lista.append(n_deporte)
    if get_lista==False:
        return xml_generador(nueva_lista)
    else:
        return xml_generador(nueva_lista),nueva_lista


def xml_generador(lista_deportes = []):
    '''Sirve para generar un xml de deportes a partir de una lista de objetos XMLDeporte

    Parámetros:
    lista_deportes -- contiene una lista de XMLDeporte
    
    Retorna:
    una cadena con el XML que representa a todos los objetos
    '''
    xml_i = '<?xml version="1.0" encoding="UTF-8"?>\n'
    xml = xml_i+'<deportes>\n'
    for deporte in lista_deportes:
        xml+=deporte.toxml()
        xml+='\n\n'
    xml+= '</deportes>'
    return xml


def xml_parser(cadena_xml):
    '''
    Parsea la cadena y retorna un objeto XMLDeporte.
    Retorna None en situación de error.
    
    Parámetros:
    cadena_xml -- cadena con el xml a parsear
    '''
    #crea un nuevo objeto para retornar
    xmlDeporte = XMLDeporte()
    
    #parseo del xml
    #try:
    cadena = cadena_xml
    cadena = cadena.replace('\n',' ')
    cadena = smart_str(cadena)#cadena.decode('utf8')
    if len(cadena)==0:
        return []
    
    dom_xml = parseString(cadena)
    deportes = dom_xml.getElementsByTagName('deportes')
    dom_deporte = deportes[0].getElementsByTagName('deporte')
    if len(dom_deporte)==1:
        deporte = dom_deporte[0]
    else:
        lista_deportes = []
        for deporte in dom_deporte:
            xmlDeporte = XMLDeporte()
            #cambiar el nombre del deporte
            xmlDeporte.setNombreDom(deporte)
            #cambiar los datos requeridos
            xmlDeporte.setDatos_requeridosDom(deporte)
            #cambiar los datos requeridos
            xmlDeporte.setDisciplinasDom(deporte)
            lista_deportes.append(xmlDeporte)
        return lista_deportes
    #except:
        #la estructura del xml no es la adecuada
    #    print 'La estructura del xml no es adecuada'
    #    return None
    
    #cambiar el nombre del deporte
    xmlDeporte.setNombreDom(deporte)
    #cambiar los datos requeridos
    xmlDeporte.setDatos_requeridosDom(deporte)
    #cambiar los datos requeridos
    xmlDeporte.setDisciplinasDom(deporte)
    return [xmlDeporte]
